package com.tanhua.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.admin.mapper.AdminUsersMapper;
import com.tanhua.admin.pojo.AdminUser;
import com.tanhua.admin.utils.AdminUserThreadLocal;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName SystemService
 * @Description TODO
 * @Author JavaDev
 * @Date 2021/1/16 21:12
 * @Version 1.0
 */
@Service
@Slf4j
public class SystemService {

    @Value("${jwt.secret}")
    private String secret;

    @Autowired
    AdminUsersMapper adminUsersMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 用户登录 校验账户密码并生成token
     *
     * @param username
     * @param password
     * @return
     */
    public Map<String, Object> login(String username, String password) {
        AdminUser adminUser = adminUsersMapper.selectOne(new LambdaQueryWrapper<AdminUser>().eq(AdminUser::getUsername, username).eq(AdminUser::getPassword, password));
        if (adminUser != null) {

            Map<String, Object> claims = new HashMap<>(2);
            claims.put("uid", adminUser.getUid());
            claims.put("username", adminUser.getUsername());
            /** 生成 token */
            String token = Jwts.builder()
                    /** 设置响应数据体 */
                    .setClaims(claims)
                    /** 设置加密方法和盐值 */
                    .signWith(SignatureAlgorithm.HS256, secret)
                    /** 设置 token 失效时间为12小时 */
                    .setExpiration(new DateTime().plusHours(12).toDate())
                    .compact();
            Map<String, Object> result = new HashMap<>(1);
            result.put("token", token);
            return result;
        } else {
            return null;
        }

    }

    public AdminUser queryUserByToken(String token) {
        try {
            /** 通过 JWT 解析 token 数据
             * {mobile=13333333333,id=2,exp=1605513392}
             * */
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();

            AdminUser adminUser = AdminUser.builder().uid(Long.valueOf(String.valueOf(body.get("uid")))).username(String.valueOf(body.get("username"))).build();

            /**
             * 需要返回 user 对象中的 mobile, 需要查询数据库获取到 mobile 数据
             * 如果每次都查询数据库,必然会导致性能问题,需要对手机号进行缓存操作
             * 数据缓存时,需要设置过期时间,过期时间要与token的时间一致
             */
            String redisKey = "TANHUA_ADMIN_USER_USERNAME_" + adminUser.getUid();
            /** 如果在redis缓存中查询到了该键,就设置返回值user中的mobile字段 */
            if (redisTemplate.hasKey(redisKey)) {
                String adminUserStr = (String) redisTemplate.opsForValue().get(redisKey);
                adminUser = new ObjectMapper().readValue(adminUserStr, new TypeReference<AdminUser>() {
                });
            } else {
                /** 如果没有在缓存中查找到,我们先从数据库中查询出该手机号并写入到redis中去 */
                adminUser = adminUsersMapper.selectOne(new LambdaQueryWrapper<AdminUser>().eq(AdminUser::getUid, adminUser.getUid()));
                adminUser.setRoles(adminUser.getRolesStr().split(","));

                /** 将手机号写入到redis中
                 * 注意JWT中过期的时间单位为秒
                 * */
                Long timeout = Long.valueOf(body.get("exp").toString()) * 1000 - System.currentTimeMillis();
                redisTemplate.opsForValue().set(redisKey, new ObjectMapper().writeValueAsString(adminUser), timeout, TimeUnit.MILLISECONDS);


            }

            return adminUser;
        } catch (ExpiredJwtException e) {
            log.error("token已过期", e);
        } catch (Exception e) {
            log.error("token不合法", e);
        }
        return null;
    }

    public Boolean logout() {
        AdminUser adminUser = AdminUserThreadLocal.get();
        String redisKey = "TANHUA_ADMIN_USER_USERNAME_" + adminUser.getUid();

        return redisTemplate.delete(redisKey);
    }
}